package com.waves.spring.boot.run;

import lombok.SneakyThrows;
import org.springframework.boot.DefaultBootstrapContext;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.context.config.ConfigDataEnvironmentPostProcessor;
import org.springframework.boot.context.event.EventPublishingRunListener;
import org.springframework.boot.context.properties.bind.Bindable;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.context.properties.source.ConfigurationPropertySources;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.boot.env.EnvironmentPostProcessorApplicationListener;
import org.springframework.boot.logging.DeferredLogs;
import org.springframework.core.env.SimpleCommandLinePropertySource;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.support.ResourcePropertySource;
import org.springframework.core.io.support.SpringFactoriesLoader;

import java.lang.reflect.Field;
import java.util.List;

/**
 * 3-6准备 Environment环境对象，并添加来源
 */
public class RunApplication3 {
    @SneakyThrows
    public static void main(String[] args) {
        ApplicationEnvironment env = new ApplicationEnvironment();
        // 查询环境变量
        System.out.println(env.getProperty("JAVA_HOME"));
        // 4.添加 ConfigurationPropertySources 管理配置信息
        ConfigurationPropertySources.attach(env);

        // 3.创建Environment环境对象，添加命令行配置来源
        env.getPropertySources().addLast(new SimpleCommandLinePropertySource(args));

        // 手动添加配置文件来源，查询配置文件信息
        env.getPropertySources()
                .addLast(new ResourcePropertySource("application",new ClassPathResource("application.yml")));
        System.out.println(env.getProperty("host"));
        // 5.添加环境对象的一个后置处理器（会自动添加一个配置来源）
        // 还有一个RandomValuePropertySourceEnvironmentPostProcessor后置处理器，可以用来生成随机数
        ConfigDataEnvironmentPostProcessor processor = new ConfigDataEnvironmentPostProcessor(
                new DeferredLogs(), new DefaultBootstrapContext()
        );
        SpringApplication app = new SpringApplication();
        processor.postProcessEnvironment(env, app);
        System.out.println(env.getProperty("spring.redis.host"));

        // 环境后置处理器底层介绍
        // 当然springboot底层不可能一个个添加，而是通过获取EnvironmentPostProcessor类的实现类，进行添加
//        List<String> names = SpringFactoriesLoader.loadFactoryNames(EnvironmentPostProcessor.class, RunApplication3.class.getClassLoader());
        // 上面这一步是谁完成的呢，是通过环境后置处理器的的监听器完成的
//        app.addListeners(new EnvironmentPostProcessorApplicationListener());
        // 当然监听的前提就是你得发布，使用EventPublishingRunListener发布
//        EventPublishingRunListener publisher = new EventPublishingRunListener(app, args);
//        publisher.environmentPrepared(new DefaultBootstrapContext(), env);

        // 6.将配置文件信息绑定到实体对象中
        User user = Binder.get(env).bind("user", User.class).get();
        System.out.println(user);
        // 绑定 spring.main 前缀的配置信息到 SpringApplication 对象
        env.getPropertySources().addLast(
                new ResourcePropertySource("springMain", new ClassPathResource("springMain.properties"))
        );
        Class<? extends SpringApplication> clazz = app.getClass();
        Field bannerMode = clazz.getDeclaredField("bannerMode");
        bannerMode.setAccessible(true);
        Field lazyInitialization = clazz.getDeclaredField("lazyInitialization");
        lazyInitialization.setAccessible(true);
        System.out.println(bannerMode.get(app));
        System.out.println(lazyInitialization.get(app));
        Binder.get(env).bind("spring.main", Bindable.ofInstance(app));
        System.out.println(bannerMode.get(app));
        System.out.println(lazyInitialization.get(app));

        // 打印
        env.getPropertySources().forEach(System.out::println);

    }
}
